/*
 * Copyright 2011-2012 by The Regents of the University of California Licensed
 * under the Apache License, Version 2.0 (the "License"); you may not use this
 * file except in compliance with the License. you may obtain a copy of the
 * License from
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package edu.ucsd.db.bassarisk.query;

import java.util.List;

import edu.ucsd.db.bassarisk.exception.VisitorException;
import edu.ucsd.db.bassarisk.query.ast.SyntaxExpressionVisitor;
import edu.ucsd.db.bassarisk.query.ast.SyntaxExpressions.NonterminalSyntaxExpression;
import edu.ucsd.db.bassarisk.query.ast.SyntaxExpressions.TerminalSyntaxExpression;
import edu.ucsd.db.bassarisk.query.expression.Expressions.KeywordMatchExpr;
import edu.ucsd.db.bassarisk.query.expression.Expressions.NonterminalExpr;
import edu.ucsd.db.bassarisk.query.expression.Expressions.PositionMaxSeparation;
import edu.ucsd.db.bassarisk.query.expression.Expressions.PositionMinSeparation;
import edu.ucsd.db.bassarisk.query.expression.Expressions.PositionSeparation;
import edu.ucsd.db.bassarisk.query.expression.Expressions.PositionWindow;
import edu.ucsd.db.bassarisk.query.expression.Expressions.StructureFind;
import edu.ucsd.db.bassarisk.query.expression.Expressions.StructureIn;
import edu.ucsd.db.bassarisk.query.expression.Expressions.StructureSame;
import edu.ucsd.db.bassarisk.query.expression.visitor.SupertypeDeferingExpressionTreeVisitor;

public class QueryFormattingTraversal extends
    SupertypeDeferingExpressionTreeVisitor<Void, StringBuilder> implements
    SyntaxExpressionVisitor<Void, StringBuilder> {
  private Void print(final StringBuilder sb, final String name, final Object[] args,
                     final List<? extends QueryExpression> subExprs)
      throws VisitorException {
    sb.append(name);
    for (Object arg2 : args) {
      sb.append(":").append(arg2.toString());
    }
    sb.append("(");
    for (int i = 0; i < subExprs.size(); ++i) {
      if (i > 0) {
        sb.append(" ");
      }
      QueryExpression subExpr = subExprs.get(i);
      subExpr.accept(this, sb);
    }
    sb.append(")");
    return null;
  }

  @Override
  public Void visitKeywordMatchExpr(final KeywordMatchExpr expr,
                                    final StringBuilder arg) {
    arg.append(expr.getToken());
    if (expr.isReferenced()) {
      arg.append("$").append(expr.getReference());
    }
    return null;
  }

  @Override
  public Void
      visitNonterminalAbstractExpression(final NonterminalSyntaxExpression expr,
                                         final StringBuilder arg)
          throws VisitorException {
    return this.print(arg, expr.getName(), expr.getArguments().toArray(),
                      expr.getSubExpressions());
  }

  @Override
  public Void visitNonterminalExpr(final NonterminalExpr expr,
                                   final StringBuilder arg) throws VisitorException {
    return this.print(arg, expr.functionName(), new Object[] {}, expr.getSubExprs());
  }

  @Override
  public Void visitPositionMaxSeparation(final PositionMaxSeparation expr,
                                         final StringBuilder arg)
      throws VisitorException {
    return this.print(arg,
                      expr.functionName(),
                      new Object[] { expr.getDistanceConstraint(),
                          expr.getRunClassName() }, expr.getSubExprs());
  }

  @Override
  public Void visitPositionMinSeparation(final PositionMinSeparation expr,
                                         final StringBuilder arg)
      throws VisitorException {
    return this.print(arg,
                      expr.functionName(),
                      new Object[] { expr.getDistanceConstraint(),
                          expr.getRunClassName() }, expr.getSubExprs());
  }

  @Override
  public Void visitPositionSeparation(final PositionSeparation expr,
                                      final StringBuilder arg)
      throws VisitorException {
    return this.print(arg,
                      expr.functionName(),
                      new Object[] { expr.getDistanceConstraint(),
                          expr.getRunClassName() }, expr.getSubExprs());
  }

  @Override
  public Void
      visitPositionWindow(final PositionWindow expr, final StringBuilder arg)
          throws VisitorException {
    return this.print(arg,
                      expr.functionName(),
                      new Object[] { expr.getDistanceConstraint(),
                          expr.getRunClassName() }, expr.getSubExprs());
  }

  @Override
  public Void visitStructureFind(final StructureFind expr, final StringBuilder arg)
      throws VisitorException {
    return this.print(arg, expr.functionName(),
                      new Object[] { expr.getRunClassName() }, expr.getSubExprs());
  }

  @Override
  public Void visitStructureIn(final StructureIn expr, final StringBuilder arg)
      throws VisitorException {
    return this.print(arg, expr.functionName(),
                      new Object[] { expr.getRunClassName() }, expr.getSubExprs());
  }

  @Override
  public Void visitStructureSame(final StructureSame expr, final StringBuilder arg)
      throws VisitorException {
    return this.print(arg, expr.functionName(),
                      new Object[] { expr.getRunClassName() }, expr.getSubExprs());
  }

  @Override
  public Void visitTerminalAbstractExpression(final TerminalSyntaxExpression expr,
                                              final StringBuilder arg)
      throws VisitorException {
    arg.append(expr.getToken());
    if (expr.isReferenced()) {
      arg.append("$").append(expr.getReference());
    }
    return null;
  }
}
